package org.jheuristics.ga.operators.selectors;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.epanetgrid.otimizacao.ag.InviaveisSelector;
import org.epanetgrid.otimizacao.ag.ViaveisSelector;
import org.epanetgrid.otimizacao.log.AGLogger;
import org.jheuristics.Individual;
import org.jheuristics.ga.DefaultPopulation;
import org.jheuristics.ga.GAConfig;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.Population;
import org.jheuristics.ga.multiObjective.DefaultMultiObjetiveFitness;
import org.jheuristics.ga.operators.Selector;


/**
 * TODO
 *
 * @author Vin�cius Ferraz
 */
public class PopExternaSelector implements Selector {

	private AGLogger logger = AGLogger.getInstance();
	
	/**
	 * TODO
	 *
	 * @param population
	 * @param howMany
	 * @param status
	 * @param config
	 * @return
	 * @see org.jheuristics.ga.operators.Selector#select(org.jheuristics.ga.Population, int, org.jheuristics.ga.GAStatus, org.jheuristics.ga.GAConfig)
	 */
	public Individual[] select(Population population, int howMany, GAStatus status, GAConfig config) {
		if (null == population) {
			throw new NullPointerException();
		}
		
		logger.debug("Iniciando Preenchimento da popula��o externa: ");
		
		DefaultPopulation v = new DefaultPopulation();
		v.addIndividuals(new ViaveisSelector().select(population, howMany, status, config));
		
		//nao dominados viaveis
		DefaultPopulation ndv = new DefaultPopulation();
		ndv.addIndividuals(new NonDominatedSelector().select(v, howMany, status, config));
		
		Population result = new DefaultPopulation();
		
		result.addIndividuals(ndv);
		
		StringBuffer buffer = new StringBuffer("\nIndiv�duos N�o Dominados Vi�veis adicionados: \n");
		for (Individual ind : ndv.toIndividualArray()) {
			buffer.append(ind);
			buffer.append("\n");
		}
		
		//esta com os nao dominados viaveis
		if(result.size() >= howMany) {
			logger.debug(buffer);
			return result.toIndividualArray();
		}

		//adicionando dominados viaveis
		DefaultPopulation dv = new DefaultPopulation();
		dv.addAll(Arrays.asList(new DominatedSelector().select(v, howMany, status, config)));
		
		buffer.append("Indiv�duos Dominados Vi�veis adicionados: \n");
		
		int cont = 0;
		for (int i = result.size(); i < howMany; i++) {
			//se for menor que o cont, retorna a populacao
			if(dv.size() <= cont) {
				break;
			}
			buffer.append(dv.getIndividual(cont));
			buffer.append("\n");
			
			result.addIndividual(dv.getIndividual(cont++));
			
			if(result.size() >= howMany) {
				logger.debug(buffer);
				return result.toIndividualArray();
			}
			
		}
		
		/*
		 * Melhores invi�veis nao dominados
		 */
		DefaultPopulation poptInviaveis = new DefaultPopulation();
		poptInviaveis.addIndividuals(new InviaveisSelector().select(population, 0, status, config));
		
		Individual[] poptInviaveisNaoDominados =new NonDominatedSelector().select(poptInviaveis, 0, status, config);
		
		Comparator<Individual> comparator = new Comparator<Individual>() {

			public int compare(Individual o1, Individual o2) {
				
				//falha
				List da1 = ((List)o1.getDataApplication());
				List da2 = ((List)o2.getDataApplication());
				Integer falha1 =  Integer.parseInt(da1.get(da1.size() - 3).toString());
				Integer falha2 =  Integer.parseInt(da2.get(da2.size() - 3).toString());

				//retorna a de maior ponto de falha
				if(!falha1.equals(falha2)) {
					return falha2.compareTo(falha1);
				}
				
				Integer severidade1 =  Integer.parseInt(da1.get(da1.size() - 2).toString());
				Integer severidade2 =  Integer.parseInt(da2.get(da2.size() - 2).toString());
				
				//retorna a de menor severidade
				if(!severidade1.equals(severidade2)) {
					return severidade1.compareTo(severidade2);
				}
				
				DefaultMultiObjetiveFitness f1 = ((DefaultMultiObjetiveFitness)o1.getFitness());
				DefaultMultiObjetiveFitness f2 = ((DefaultMultiObjetiveFitness)o2.getFitness());
				
				Double ap1 = f1.getDensity() + f1.getRawFitness();
				Double ap2 = f2.getDensity() + f2.getRawFitness();
				
				//aptidao em ordem crescente
				return ap1.compareTo(ap2);
			}
			
		};

		Arrays.sort(poptInviaveisNaoDominados, comparator);
		
		buffer.append("Indiv�duos N�o Dominados Invi�veis adicionados: \n");
		
		cont = 0;
		for (int i = result.size(); i < howMany; i++) {
			//se for menor que o cont, retorna a populacao
			if(poptInviaveisNaoDominados.length <= cont) {
				break;
			}
			
			buffer.append(poptInviaveisNaoDominados[cont]);
			buffer.append("\n");
			
			result.addIndividual(poptInviaveisNaoDominados[cont++]);
			
			if(result.size() >= howMany) {
				logger.debug(buffer);
				return result.toIndividualArray();
			}
			
		}
		
		//Coloca os inviaveis na ordem por: melhor ponto falha, severidade, aptid�o
		
		Individual[] poptInviaveisDominados = new DominatedSelector().select(poptInviaveis, 0, status, config);
		
		Arrays.sort(poptInviaveisDominados, comparator);
		
		buffer.append("Indiv�duos Dominados Invi�veis adicionados: \n");
		cont = 0;
		for (int i = result.size(); i < howMany; i++) {
			//se for menor que o cont, retorna a populacao
			if(poptInviaveisDominados.length <= cont) {
				break;
			}
			buffer.append(poptInviaveisDominados[cont]);
			buffer.append("\n");
			result.addIndividual(poptInviaveisDominados[cont++]);
			
			if(result.size() >= howMany) {
				logger.debug(buffer);
				return result.toIndividualArray();
			}
			
		}
		logger.debug(buffer);
		return result.toIndividualArray();
		
	}

}
